/**
 * 
 */
package com.googlecode.lohr.parser.combinators.matcher;


import java.util.concurrent.atomic.AtomicInteger;

import com.googlecode.lohr.parser.combinators.MatchError;
import com.googlecode.lohr.parser.combinators.Parsers;
import com.googlecode.lohr.parser.combinators.node.AbstractNode;

/**
 * Wraps a matcher and excludes any matches that match a given set of filters  
 * @author Ted Stockwell
 */
public final class ExcludingMatcher extends Matcher
{
	private final Matcher filterMatcher;
	private final Matcher matcher;

	public ExcludingMatcher(Matcher matcher, Matcher[] filters)
	{
		this.filterMatcher = Parsers.choice(filters);
		this.matcher= matcher;
	}

	@Override
	public void match(MatchContext ctx, int start) 
	{
		final Matcher thisMatcher= this;
		final AtomicInteger matchCount= new AtomicInteger();
		new MatchTask(ctx, matcher, start) {
			@Override
			public void onMatchFound(final AbstractNode potentialMatch) {
				matchCount.incrementAndGet();
				new MatchTask(ctx, filterMatcher, start) {
					boolean excluded= false;
					@Override public void onMatchFound(AbstractNode filterMatch) {
						if (filterMatch.getText().equals(potentialMatch.getText())) {
							excluded= true;
							stopMatching();
						}
					};
					@Override public void onMatchComplete(MatchError matchError) {
						if (!excluded)
							ctx.matchFound(thisMatcher, start, potentialMatch);
						
						if (matchCount.decrementAndGet() <= 0) {
							MatchError err= null;
							if (!ctx.hasMatches(thisMatcher, start)) 
								err= new MatchError("All matches have been excluded", start);
							ctx.matchComplete(thisMatcher, start, err);
						}
					}
				}.run();
			}
			
			public void onMatchComplete(MatchError matchError) {
				if (matchError != null) {
					// the base matcher failed
					ctx.matchComplete(thisMatcher, start, matchError);
				}
				else if (!ctx.hasMatches(matcher, start)) {
					// no matches found and no error
					ctx.matchComplete(thisMatcher, start);
				}
			};
		}.run();
	}
}